﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using SnippetBox.Core.Common.Paging;
using SnippetBox.Core.Data.Models;
using LinqKit;

namespace SnippetBox.Core.Data.Service {
    public class CategoryService : ServiceBase<Category> {
        private const int PAGE_SIZE = 10;

        public IPagedList<Category> GetAllPaged(int? page) {
            return _repository.GetAll()
                .OrderBy(x => x.Name).ThenBy(x => x.ParentCategory.Name)
                .ToPagedList(page, PAGE_SIZE);
        }

        public IPagedList<Category> GetAllMainPaged(int? page) {
            return _repository.GetAll()
                .Where(x => x.ParentCategory == null)
                .OrderBy(x => x.Name).ThenBy(x => x.ParentCategory.Name)
                .ToPagedList(page, PAGE_SIZE);
        }

        public List<Category> GetMainCategories() {
            return _repository.GetAll()
                .Where(x => x.ParentCategory == null)
                .OrderBy(x => x.Name)
                .ToList();
        }

        public List<Category> GetAllChildren(int parentId) {
            return _repository.GetAll().Where(x => x.ParentCategory.CategoryId == parentId).ToList();
        }

        public int GetTopParentOf(int categoryId) {
            var category = _repository.GetById(categoryId);
            
            if (category == null)
                return -1;

            var mainCategory = GetTopParentOfCategoryRecursive(category);
            
            if (mainCategory != null)
                return mainCategory.CategoryId;
            
            return -1;
        }

        private static Category GetTopParentOfCategoryRecursive(Category category) {
            if (category == null)
                return null;

            if (category.ParentCategory == null)
                return category;

            return GetTopParentOfCategoryRecursive(category.ParentCategory);
        }

        public IEnumerable<SelectListItem> GetCategoryList() {
            var main = GetMainCategories();
            return GetCategoryListInternal(main).ToList();
        }

        private static IEnumerable<SelectListItem> GetCategoryListInternal(IEnumerable<Category> main, int depth = 0) {
            foreach (var category in main) {
                yield return new SelectListItem 
                {
                    Text = Indent(depth) + " " + category.Name,
                    Value = category.CategoryId.ToString()
                };

                if (category.ChildrenCategories != null) {
                    foreach (var subCategory in GetCategoryListInternal(category.ChildrenCategories, depth + 1)) {
                        yield return subCategory;
                    } 
                }
            }
        }

        private static string Indent(int count) {
            StringBuilder sb = new StringBuilder(count);
            for (int i = 0; i < count; i++) {
                sb.Append("—");
            }
            return sb.ToString();
        }

        public IPagedList<Category> Search(string query, int? page, int pageSize = PAGE_SIZE, bool splitSearch = false) {
            var categories = _repository.GetAll();

            if (splitSearch && query.Contains(" ")) {
                var queryWords = query.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                var predicate = PredicateBuilder.False<Category>();
                
                foreach (var word in queryWords) {
                    string word1 = word;
                    predicate = predicate.Or(x => x.Name.Contains(word1));
                }
                
                categories = categories.AsExpandable().Where(predicate);
            }
            else {
                categories = categories.Where(x => x.Name.Contains(query));
            }

            categories = categories.OrderBy(x => x.Name);
            
            return categories.ToPagedList(page, pageSize);
        }
    }
}
